//
//  Terrain.m
//  CompGraf2
//
//  Created by Juan Niosi on 05/09/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "Terrain.h"
#import "World.h"
@implementation Terrain
@synthesize width,height;

- (id)init
{
    self = [super init];
    if (self) {
        terrainData = nil;
        height = 0;
        width = 0;
        tileWidth = 0;
        zheight = 0;
        xOffset = 0;
        yOffset = 0;
    }
    
    return self;
}

- (id)init:(NSString *)filename engine:(Engine *)engine
{
    self = [super init];
    if (self)
    {
        NSString* loc = [[NSBundle mainBundle] pathForResource:filename ofType:@""];
        NSString* contents = [NSString stringWithContentsOfFile:loc encoding:NSUTF8StringEncoding error:nil];
        children = [[NSMutableArray alloc] init];
        
        for (int i = 0; i < 4; i++)
        {
            Entity *quad = [[Entity alloc] init];
            [quad setName:[[NSString alloc] initWithFormat:@"%i",i]];
            Entity *quadWalls = [[Entity alloc] init];
            [quadWalls setName:@"wall"];
            [quad addChild:quadWalls];
            [self addChild:quad];
        }
        
        NSArray *lines = [contents componentsSeparatedByString:@"\n"];
        
        tileWidth = 0;
        zheight = 0;
        xOffset = 0;
        yOffset = 0;
        int i = 0;
        Texture *floorTex = nil;
        while (i < [lines count])
        {
            NSString *line = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
            if (line == @"")
                continue;
            
            NSArray *tokens = [line componentsSeparatedByString:@" "];
            if ([tokens count] == 0)
                continue;
            NSString *first = [tokens objectAtIndex:0];
            if ([first isEqualToString:@"width"])
            {
                width = [[tokens objectAtIndex:1] intValue];
            }
            else if ([first isEqualToString:@"height"])
            {
                height = [[tokens objectAtIndex:1] intValue];
            }
            else if ([first isEqualToString:@"separation"])
            {
                tileWidth = [[tokens objectAtIndex:1] floatValue];
            }
            else if ([first isEqualToString:@"quadtree"])
            {
                int levels = [[tokens objectAtIndex:1] intValue];
                Bounds *fullBounds = [[Bounds alloc] init];
                fullBounds.zMin = yOffset + tileWidth*0.5f;
                fullBounds.zMax = fullBounds.zMin - (tileWidth * (height));
                if (fullBounds.zMin > fullBounds.zMax)
                {
                    float temp = fullBounds.zMin;
                    fullBounds.zMin = fullBounds.zMax;
                    fullBounds.zMax = temp;
                }
                fullBounds.xMin = (float)xOffset - tileWidth*0.5f;
                fullBounds.xMax = fullBounds.xMin + (tileWidth * (width));
                fullBounds.yMin = -5.0f;
                fullBounds.yMax = 5.0f;
                root = [[QuadNode alloc] init:levels :fullBounds];
                NSLog(@"%@",[fullBounds toString]);
            }
            else if ([first isEqualToString:@"z"])
            {
                zheight = [[tokens objectAtIndex:1] floatValue];
            }
            else if ([first isEqualToString:@"xoffset"])
            {
                xOffset = [[tokens objectAtIndex:1] floatValue];
            }
            else if ([first isEqualToString:@"yoffset"])
            {
                yOffset = [[tokens objectAtIndex:1] floatValue];
            }
            else if ([first isEqualToString:@"texture"])
            {
                floorTex = [engine load_texture:[tokens objectAtIndex:1]];
            }
            else if ([first isEqualToString:@"data"])
            {
                int32_t vIdx = 0;
                int32_t uvIdx = 0;
                
                int32_t count1s = 0;
                terrainData = malloc(sizeof(int)*width*height);
                int valIdx = 0;
                for (u_int32_t j = 1; j <= height; j++)
                {
                    NSString *data = [[lines objectAtIndex:i+j] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
                    NSArray *dataTokens = [data componentsSeparatedByString:@" "];
                    for (u_int32_t k = 0; k < width; k++)
                    {
                        terrainData[valIdx] = [[dataTokens objectAtIndex:k] intValue];
                        if (terrainData[valIdx] == FLOOR)
                            count1s++;
                        valIdx++;
                    }
                }
                
                GLfloat *tempVerts = nil; malloc(sizeof(GLfloat)*count1s*18);
                GLfloat *tempUVs = malloc(sizeof(GLfloat)*count1s*12);
                GLfloat *tempNormals = malloc(sizeof(GLfloat)*count1s*18); 
                for (GLint y = 0; y < height; y++)
                {
                    for (GLint x = 0; x < width; x++)
                    {
                        if (terrainData[y*width+x] == FLOOR)
                        {
                            tempVerts = malloc(sizeof(GLfloat)*18);
                            tempUVs = malloc(sizeof(GLfloat)*12);
                            tempNormals = malloc(sizeof(GLfloat)*18); 
                            vIdx = 0;
                            uvIdx = 0;
                            
                            GLfloat y1 = -(y - 0.5f) * tileWidth + yOffset;
                            GLfloat y2 = -(y + 0.5f) * tileWidth + yOffset;
                            GLfloat x1 = (x - 0.5f) * tileWidth + xOffset;
                            GLfloat x2 = (x + 0.5f) * tileWidth + xOffset;
                            
                            tempVerts[vIdx] = x1;
                            tempNormals[vIdx] = 0.0f;
                            vIdx++;
                            tempVerts[vIdx] = zheight;
                            tempNormals[vIdx] = 1.0f;
                            vIdx++;
                            tempVerts[vIdx] = y1;
                            tempNormals[vIdx] = 0.0f;
                            vIdx++;
                            
                            tempVerts[vIdx] = x2;
                            tempNormals[vIdx] = 0.0f;
                            vIdx++;
                            tempVerts[vIdx] = zheight;
                            tempNormals[vIdx] = 1.0f;
                            vIdx++;
                            tempVerts[vIdx] = y1;
                            tempNormals[vIdx] = 0.0f;
                            vIdx++;
                            
                            tempVerts[vIdx] = x1;
                            tempNormals[vIdx] = 0.0f;
                            vIdx++;
                            tempVerts[vIdx] = zheight;
                            tempNormals[vIdx] = 1.0f;
                            vIdx++;
                            tempVerts[vIdx] = y2;
                            tempNormals[vIdx] = 0.0f;
                            vIdx++;
                            
                            tempVerts[vIdx] = x2;
                            tempNormals[vIdx] = 0.0f;
                            vIdx++;
                            tempVerts[vIdx] = zheight;
                            tempNormals[vIdx] = 1.0f;
                            vIdx++;
                            tempVerts[vIdx] = y1;
                            tempNormals[vIdx] = 0.0f;
                            vIdx++;
                            
                            tempVerts[vIdx] = x2;
                            tempNormals[vIdx] = 0.0f;
                            vIdx++;
                            tempVerts[vIdx] = zheight;
                            tempNormals[vIdx] = 1.0f;
                            vIdx++;
                            tempVerts[vIdx] = y2;
                            tempNormals[vIdx] = 0.0f;
                            vIdx++;
                            
                            tempVerts[vIdx] = x1;
                            tempNormals[vIdx] = 0.0f;
                            vIdx++;
                            tempVerts[vIdx] = zheight;
                            tempNormals[vIdx] = 1.0f;
                            vIdx++;
                            tempVerts[vIdx] = y2;
                            tempNormals[vIdx] = 0.0f;
                            vIdx++;
                            
                            GLfloat uv_x1 = x / (width + 1.0f);
                            GLfloat uv_x2 = (x + 1.0f) / (width + 1.0f);
                            
                            GLfloat uv_y1 = y / (height + 1.0f);
                            GLfloat uv_y2 = (y + 1.0f) / (height + 1.0f);
                            tempUVs[uvIdx] = uv_x1;
                            uvIdx++;
                            tempUVs[uvIdx] = uv_y1;
                            uvIdx++;
                            
                            tempUVs[uvIdx] = uv_x2;
                            uvIdx++;
                            tempUVs[uvIdx] = uv_y1;
                            uvIdx++;
                            
                            tempUVs[uvIdx] = uv_x1;
                            uvIdx++;
                            tempUVs[uvIdx] = uv_y2;
                            uvIdx++;
                            
                            tempUVs[uvIdx] = uv_x2;
                            uvIdx++;
                            tempUVs[uvIdx] = uv_y1;
                            uvIdx++;
                            
                            tempUVs[uvIdx] = uv_x2;
                            uvIdx++;
                            tempUVs[uvIdx] = uv_y2;
                            uvIdx++;
                            
                            tempUVs[uvIdx] = uv_x1;
                            uvIdx++;
                            tempUVs[uvIdx] = uv_y2;
                            uvIdx++;
                            
                            Entity *tile = [[Entity alloc] initWithVertices:tempVerts vertCount:6 setTexture:floorTex setUVs:tempUVs setNormals:tempNormals];
                            [root addEntity:tile atIndex:0];
                        }
                    }
                }
                i += height;
            }
            else if ([first isEqualToString:@"walls"])
            {
                float wallHeight = [[tokens objectAtIndex:1] floatValue];
                float wallWidth = [[tokens objectAtIndex:2] floatValue];
                
                int wallCount = 0;
                for (GLint y = 0; y < height; y++)
                {
                    for (GLint x = 0; x < width; x++)
                    {
                        GLint pos = y*width+x; 
                        if (terrainData[pos] == FLOOR)
                        {
                            if (x == 0 || terrainData[pos-1] == HOLE)
                                wallCount++;
                            
                            if (y == 0 || terrainData[pos-width] == HOLE)
                                wallCount++;
                            
                            if (x == width-1 || terrainData[pos+1] == HOLE)
                                wallCount++;
                            
                            if (y == height-1 || terrainData[pos+width] == HOLE)
                                wallCount++;
                        }
                    }
                }
                wallCount *= 3;
                GLfloat *wallVerts = nil;
                GLfloat *wallNormals = nil;
                GLfloat *wallUVs = nil;
                int vIdx = 0;
                int uvIdx = 0;
                GLfloat y1 = zheight;
                GLfloat y2 = zheight + wallHeight;
                Texture *wallTex = [engine load_texture:[tokens objectAtIndex:3]];
                for (GLint z = 0; z < height; z++)
                {
                    for (GLint x = 0; x < width; x++)
                    {
                        if (terrainData[z*width+x] == FLOOR)
                        {
                            GLfloat z1 = -(z - 0.5f)*tileWidth + yOffset;
                            GLfloat z2 = -(z + 0.5f)*tileWidth + yOffset;
                            
                            GLfloat x1 = (x - 0.5f) * tileWidth + xOffset;
                            GLfloat x2 = (x + 0.5f) * tileWidth + xOffset;
                            
                            if (x == 0 || terrainData[z*width+(x-1)] == HOLE)
                            {
                                wallVerts = malloc(sizeof(GLfloat)*54);
                                wallNormals = malloc(sizeof(GLfloat)*54);
                                wallUVs = malloc(sizeof(GLfloat)*36);
                                vIdx = 0;
                                uvIdx = 0;
                                
                                bool isLeftOuter = z == 0 || terrainData[(z-1)*width+x] == HOLE;
                                
                                bool isRightOuter = z == height-1 || terrainData[(z+1)*width+x] == HOLE;
                                
                                //Front wall
                                wallVerts[vIdx] = x1;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = y1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x1;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = y1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x1;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x1;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x1;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = y1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x1;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                //uvs
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                //Top wall
                                wallVerts[vIdx] = x1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x1 - wallWidth;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = isLeftOuter ? z1+wallWidth : z1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x1 - wallWidth;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = isLeftOuter ? z1+wallWidth : z1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x1 - wallWidth;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = isRightOuter ? z2-wallWidth : z2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                //uvs
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                //Back wall
                                wallVerts[vIdx] = x1 - wallWidth;
                                wallNormals[vIdx] = -1.0f;
                                vIdx++;
                                wallVerts[vIdx] = y1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = isLeftOuter ? z1+wallWidth : z1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x1 - wallWidth;
                                wallNormals[vIdx] = -1.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = isLeftOuter ? z1+wallWidth : z1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x1 - wallWidth;
                                wallNormals[vIdx] = -1.0f;
                                vIdx++;
                                wallVerts[vIdx] = y1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = isRightOuter ? z2-wallWidth : z2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x1 - wallWidth;
                                wallNormals[vIdx] = -1.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = isLeftOuter ? z1+wallWidth : z1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x1 - wallWidth;
                                wallNormals[vIdx] = -1.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = isRightOuter ? z2-wallWidth : z2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x1 - wallWidth;
                                wallNormals[vIdx] = -1.0f;
                                vIdx++;
                                wallVerts[vIdx] = y1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = isRightOuter ? z2-wallWidth : z2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                //uvs
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                Entity *wall = [[Entity alloc] initWithVertices:wallVerts vertCount:18 setTexture:wallTex setUVs:wallUVs setNormals:wallNormals];
                                [root addEntity:wall atIndex:1];
                            }
                            
                            if (z == 0 || terrainData[(z-1)*width+x] == HOLE)
                            {
                                
                                bool isLeftOuter = x == 0 || terrainData[z*width+(x-1)] == HOLE;
                                
                                bool isRightOuter = x == width-1 || terrainData[z*width+(x+1)] == HOLE;
                                
                                wallVerts = malloc(sizeof(GLfloat)*54);
                                wallNormals = malloc(sizeof(GLfloat)*54);
                                wallUVs = malloc(sizeof(GLfloat)*36);
                                vIdx = 0;
                                uvIdx = 0;
                                
                                //front wall
                                wallVerts[vIdx] = x1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1;
                                wallNormals[vIdx] = -1.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1;
                                wallNormals[vIdx] = -1.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1;
                                wallNormals[vIdx] = -1.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1;
                                wallNormals[vIdx] = -1.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1;
                                wallNormals[vIdx] = -1.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1;
                                wallNormals[vIdx] = -1.0f;
                                vIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                //top wall
                                wallVerts[vIdx] = x1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = isLeftOuter ? x1-wallWidth : x1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1 + wallWidth;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = isLeftOuter ? x1-wallWidth : x1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1 + wallWidth;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = isRightOuter ? x2+wallWidth : x2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1 + wallWidth;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                //back wall
                                wallVerts[vIdx] = isLeftOuter ? x1-wallWidth : x1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1 + wallWidth;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = isRightOuter ? x2+wallWidth : x2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1 + wallWidth;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = isLeftOuter ? x1-wallWidth : x1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1 + wallWidth;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = isRightOuter ? x2+wallWidth : x2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1 + wallWidth;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = isRightOuter ? x2+wallWidth : x2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1 + wallWidth;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = isLeftOuter ? x1-wallWidth : x1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1 + wallWidth;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                Entity *wall = [[Entity alloc] initWithVertices:wallVerts vertCount:18 setTexture:wallTex setUVs:wallUVs setNormals:wallNormals];
                                [root addEntity:wall atIndex:1];
                            }
                            
                            if (x == width-1 || terrainData[z*width+(x+1)] == HOLE)
                            {
                                wallVerts = malloc(sizeof(GLfloat)*54);
                                wallNormals = malloc(sizeof(GLfloat)*54);
                                wallUVs = malloc(sizeof(GLfloat)*36);
                                vIdx = 0;
                                uvIdx = 0;
                                
                                bool isLeftOuter = z == 0 || terrainData[(z-1)*width+x] == HOLE;
                                
                                bool isRightOuter = z == height-1 || terrainData[(z+1)*width+x] == HOLE;
                                
                                //front wall
                                wallVerts[vIdx] = x2;
                                wallNormals[vIdx] = -1.0f;
                                vIdx++;
                                wallVerts[vIdx] = y1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x2;
                                wallNormals[vIdx] = -1.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x2;
                                wallNormals[vIdx] = -1.0f;
                                vIdx++;
                                wallVerts[vIdx] = y1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x2;
                                wallNormals[vIdx] = -1.0f;
                                vIdx++;
                                wallVerts[vIdx] = y1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x2;
                                wallNormals[vIdx] = -1.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x2;
                                wallNormals[vIdx] = -1.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                //Top wall
                                wallVerts[vIdx] = x2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = z1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x2 + wallWidth;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = isLeftOuter ? z1+wallWidth : z1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x2 + wallWidth;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = isLeftOuter ? z1+wallWidth : z1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x2 + wallWidth;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = isRightOuter ? z2-wallWidth : z2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                //uvs
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                //Back wall
                                wallVerts[vIdx] = x2 + wallWidth;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = y1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = isLeftOuter ? z1+wallWidth : z1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x2 + wallWidth;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = y1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = isRightOuter ? z2-wallWidth : z2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x2 + wallWidth;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = isLeftOuter ? z1+wallWidth : z1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x2 + wallWidth;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = isLeftOuter ? z1+wallWidth : z1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x2 + wallWidth;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = y1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = isRightOuter ? z2-wallWidth : z2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x2 + wallWidth;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = isRightOuter ? z2-wallWidth : z2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                //uvs
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                Entity *wall = [[Entity alloc] initWithVertices:wallVerts vertCount:18 setTexture:wallTex setUVs:wallUVs setNormals:wallNormals];
                                [root addEntity:wall atIndex:1];
                            }
                            
                            if (z == height-1 || terrainData[(z+1)*width+x] == HOLE)
                            {
                                wallVerts = malloc(sizeof(GLfloat)*54);
                                wallNormals = malloc(sizeof(GLfloat)*54);
                                wallUVs = malloc(sizeof(GLfloat)*36);
                                vIdx = 0;
                                uvIdx = 0;
                                
                                bool isLeftOuter = x == 0 || terrainData[z*width+(x-1)] == HOLE;
                                
                                bool isRightOuter = x == width-1 || terrainData[z*width+(x+1)] == HOLE;
                                
                                //front wall
                                wallVerts[vIdx] = x1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                //top wall
                                wallVerts[vIdx] = x1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = isLeftOuter ? x1-wallWidth : x1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2 - wallWidth;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = isLeftOuter ? x1-wallWidth : x1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2 - wallWidth;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = x2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = isRightOuter ? x2+wallWidth : x2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 1.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2 - wallWidth;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                //back wall
                                wallVerts[vIdx] = isLeftOuter ? x1-wallWidth : x1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2 - wallWidth;
                                wallNormals[vIdx] = -1.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = isLeftOuter ? x1-wallWidth : x1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2 - wallWidth;
                                wallNormals[vIdx] = -1.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = isRightOuter ? x2+wallWidth : x2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2 - wallWidth;
                                wallNormals[vIdx] = -1.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = isRightOuter ? x2+wallWidth : x2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2 - wallWidth;
                                wallNormals[vIdx] = -1.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = isLeftOuter ? x1-wallWidth : x1;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2 - wallWidth;
                                wallNormals[vIdx] = -1.0f;
                                vIdx++;
                                
                                wallVerts[vIdx] = isRightOuter ? x2+wallWidth : x2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = y2;
                                wallNormals[vIdx] = 0.0f;
                                vIdx++;
                                wallVerts[vIdx] = z2 - wallWidth;
                                wallNormals[vIdx] = -1.0f;
                                vIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 0.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                wallUVs[uvIdx] = 1.0f;
                                uvIdx++;
                                
                                Entity *wall = [[Entity alloc] initWithVertices:wallVerts vertCount:18 setTexture:wallTex setUVs:wallUVs setNormals:wallNormals];
                                [root addEntity:wall atIndex:1];
                            }
                        }
                    }
                }
                [root optimize];
            }
            i++;
        }
    }
    return self;
}

- (void)draw:(Engine *)engine:(Camera *)cam
{
    [self applyTransform:engine];
    
    [root draw:engine :cam];
    
    [self resetTransform:engine];
}

- (NSArray *)getPath:(MapNode*)initial:(MapNode*)final
{
    
    if ([initial equals:final])
    {
        return [[NSArray alloc] init];
    }
    
    [initial calcH:final];
    [initial setParent:NULL];

    NSMutableArray *open = [[NSMutableArray alloc] init];
    [open addObject:initial];
    NSMutableArray *closed = [[NSMutableArray alloc] init];
    MapNode *current = NULL;
    
    while ([open count] > 0)
    {
        current = [open objectAtIndex:0];
        [open removeObjectAtIndex:0];
        [closed addObject:current];
        if ([current equals:final])
        {
            NSArray *path = [current getHierarchy];
            for (MapNode *n in path)
            {
                [n setWorldPos:[self mapNode2vert:n]];
            }
            //NSLog(@"Path from (%i,%i) to (%i,%i)",initial.x,initial.y,final.x,final.y);
            return path;
        }
        
        NSArray *adjacents = [self getAdjacents:current finalNode:final];
        int adjIdx = 0;
        for (MapNode *adj in adjacents)
        {
            if (terrainData[adj.x + adj.y*width] == HOLE || [self nodeInArray:closed :adj])
            {
                continue;
            }
            else if (![self nodeInArray:open :adj])
            {
                [self insertOrdenado:open :adj];
            }
            else
            {
                MapNode *newNode = [[MapNode alloc] initWithCoords:adj.x :adj.y setParent:current];
                if (newNode.g > adj.g)
                {
                    [adj updateParent:current];
                    [open removeObject:adj];
                    [self insertOrdenado:open :adj];
                }
            }
            adjIdx++;
        }
    }
    
    //Could not find path
    NSLog(@"WARNING: NULL PATH");
    return nil;
}

- (void)insertOrdenado:(NSMutableArray *)array:(MapNode *)nodo
{
    //Insertar ordenado
    if ([array count] == 0)
    {
        [array addObject:nodo];
    }
    else if ([array count] == 1)
    {
        if ([[array objectAtIndex:0] getF] < [nodo getF])
        {
            [array addObject:nodo];
        }
        else
        {
            [array insertObject:nodo atIndex:0];
        }
    }
    else
    {
        int idx = 0;
        int count = [array count];
        MapNode *prev = NULL;
        do
        {
            prev = [array objectAtIndex:idx];
            idx++;
        }
        while (idx < count && [prev getF] < [nodo getF]);
        if (idx >= count && [prev getF] < [nodo getF])
        {
            [array addObject:nodo];
        }
        else
        {
            idx--;
            [array insertObject:nodo atIndex:idx];
        }
    }
}

- (BOOL)nodeInArray:(NSArray *)array:(MapNode *)node
{
    for (MapNode *an in array)
    {
        if ([an equals:node])
            return true;
    }
    return false;
}

- (NSArray *)getAdjacents:(MapNode*)node finalNode:(MapNode *)final
{
    NSMutableArray *adjacents = [[NSMutableArray alloc] init];
    for (int i = -1; i <= 1; i++)
    {
        if (node.x + i < 0 || node.x + i >= width)
            continue;
        
        for (int j = -1; j <= 1; j++)
        {
            if (node.y + j < 0 || node.y + j >= height)
                continue;
            
            if (j == 0 && i == 0)
                continue;
            
            int x = node.x + i;
            int y = node.y + j;
            
            if (terrainData[x + y*width] == FLOOR)
            {
                //Diagonal
                if (abs(i) + abs(j) == 2)
                {
                    //No side walls
                    if (terrainData[node.x + y*width] == FLOOR && terrainData[x + node.y*width] == FLOOR)
                    {
                        MapNode *adj = [[MapNode alloc] initWithCoords:x :y setParent:node];
                        [adj calcH:final];
                        [adjacents addObject:adj];
                    }
                }
                //Straight
                else
                {
                    MapNode *adj = [[MapNode alloc] initWithCoords:x :y setParent:node];
                    [adj calcH:final];
                    [adjacents addObject:adj];
                }
            }
        }
    }
    return [[NSArray alloc] initWithArray:adjacents];
}

- (BOOL)isOccupied:(int)x:(int)y
{
    World *world = [World getWorld];
    MapNode *curr = [world.mainCharacter getCurrentObjective];
    if ([curr equals:x :y])
        return true;
    
    for (Enemy *enm in world.enemies)
    {
        curr = [enm getCurrentObjective];
        if ([curr equals:x :y])
            return true;
    }
    
    return false;
}

- (GLfloat *)posToCoords:(int)x:(int)y
{
    GLfloat *coords = malloc(sizeof(GLfloat)*3);
    coords[0] = x*tileWidth + xOffset;
    coords[1] = zheight + 0.1f;
    coords[2] = -y*tileWidth + yOffset;
    
    return coords;
}

- (void)drawPath:(NSArray *)path withEngine:(Engine *)engine
{
    [engine set_transform];
    if ([path count] < 2)
        return;
    
    MapNode* node = (MapNode *)[path objectAtIndex:0];
    GLfloat *prevCoords = [node.worldPos getValues];
    GLfloat *curCoords;
    for (int i = 1; i < [path count]; i++)
    {
        
        node = (MapNode *)[path objectAtIndex:i];
        curCoords = [node.worldPos getValues];
        
        [engine drawLine:prevCoords :curCoords];
        
        prevCoords = curCoords;
    }
    
    prevCoords = NULL;
    curCoords = NULL;
    [engine reset_transform];
}

- (Vector3 *)mapNode2vert:(MapNode *)pn
{
    return [[Vector3 alloc] init:[self posToCoords:pn.x :pn.y]];
}

- (MapNode *)vert2MapNode:(Vector3 *)vec
{
    int x = (int)roundf(([vec getX] - xOffset) / tileWidth);
    int z = (int)roundf(-([vec getZ] - yOffset) / tileWidth);
    MapNode *node = [[MapNode alloc] initWithCoords:x :z];
    [node setWorldPos:vec];
    return node;
}

- (void)dealloc
{
    free(terrainData);
    free(root);
    
    [super dealloc];
}
@end
